﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using PMisuseAn.io;
using System.Collections;
using System.Windows;

namespace PMisuseAn.behaviour.model
{
    public class Context
    {
        private static List<Agent> agents;
        private static List<Attribute> environmentalAttributes;
        private static List<Attribute> agentAttributes;
        private static List<Attribute> regulatoryAttributes;
       
        private static List<string> groupFunctions;
        private static List<string> groupNames;
        
        private static Random r_env_vars = new Random();
        private static Random r_agent_vars = new Random();
        private static Random r_sm_vars = new Random();
        private static Random r_his_var = new Random();
        private static int intCount;

        Random ra_gf = new Random();
        Random ra_g = new Random();

        //Random r_a;

        public static bool isStateMessage;
        public static bool isTransitionMessage;

        int defaultSeed;

        //transmission history: Dictionary<context, interactionInstance>()
        //public static Dictionary<List<Object>, Dictionary<Dictionary<Agent, Agent>, ListDictionary>> transmissionHistory;
        //public Stack<ListDictionary> transmissionHistory;

        public List<Object> currentParameterisedContextInstances;
        public Dictionary<Dictionary<Agent, Agent>, ListDictionary> currentSendReceiveContextAgent;

        public Context(List<Agent> _agents, List<string> _groupFunctions, List<string> _groupNames)
        {
            agents = _agents;
            environmentalAttributes = MisUseAnObjectsDB.loadEnvironmentAttributes(MainWindow.clusterName);
            agentAttributes = MisUseAnObjectsDB.loadAgentAttributes(MainWindow.clusterName);
            regulatoryAttributes = MisUseAnObjectsDB.loadRegulatoryAttributes(MainWindow.clusterName);
            //transmissionHistory = new Dictionary<List<Object>, Dictionary<Dictionary<Agent, Agent>, ListDictionary>>();
            //transmissionHistory = new Stack<ListDictionary>();

            groupFunctions = _groupFunctions;
            groupNames = _groupNames;

        }
        
        public List<Object> generateParamaterisedEnvAttInstances()
        {
            List<Object> parameterisedInstances = new List<Object>();
            int no_env_context_vars = r_env_vars.Next(environmentalAttributes.Count);
            //bool invalidSeed = true;
            //while (invalidSeed)
            //{
            //    if (environmentalAttributes.Count == 0)
            //    {
            //        invalidSeed = false;
            //    }
            //    else if (environmentalAttributes.Count > 0 && no_env_context_vars == 0)
            //    {
            //        no_env_context_vars = r_env_vars.Next(environmentalAttributes.Count);
            //    }
            //    else if (environmentalAttributes.Count > 0 && no_env_context_vars > 0)
            //    {
            //        invalidSeed = false;
            //    }
            //}
            defaultSeed = no_env_context_vars;

            //for (int i = 0; i < no_env_context_vars; i++)
            //{
            //    Attribute envAtt = environmentalAttributes[i];
            int seed = no_env_context_vars;
            Random r_a = new Random(seed);
            //    if (envAtt.domain.Count > 0)
            //    {
            //        envAtt.activeDomain = envAtt.domain[r_a.Next(envAtt.domain.Count)];
            //    }

            //    parameterisedInstances.Add(envAtt); 
            //}

            foreach (Attribute att in environmentalAttributes)
            {
                if (att.domain.Count > 0)
                {
                    att.activeDomain = att.domain[r_a.Next(att.domain.Count)];
                    
                    bool exist = false;
                    foreach (Object obj in parameterisedInstances)
                    {
                        Attribute attx = obj as Attribute;
                        if (attx != null)
                        {
                            if (attx.name.Equals(att.name))
                            {
                                exist = true;
                                break;
                            }
                        }
                    }
                    if (!exist)
                    {
                        parameterisedInstances.Add(att);
                    }
                }
            }

            //paramaterise agent attributes
            foreach (Attribute att in agentAttributes)
            {
                if (att.domain.Count > 0)
                {
                    att.activeDomain = att.domain[r_a.Next(att.domain.Count)];

                    bool exist = false;
                    foreach (Object obj in parameterisedInstances)
                    {
                        Attribute attx = obj as Attribute;
                        if (attx != null)
                        {
                            if (attx.name.Equals(att.name))
                            {
                                exist = true;
                                break;
                            }
                        }
                    }
                    if (!exist)
                    {
                        parameterisedInstances.Add(att);
                    }
                }
            }

            //paramaterise regulatory attributes
            foreach (Attribute att in regulatoryAttributes)
            {
                if (att.domain.Count > 0)
                {
                    att.activeDomain = att.domain[r_a.Next(att.domain.Count)];

                    bool exist = false;
                    foreach (Object obj in parameterisedInstances)
                    {
                        Attribute attx = obj as Attribute;
                        if (attx != null)
                        {
                            if (attx.name.Equals(att.name))
                            {
                                exist = true;
                                break;
                            }
                        }
                    }
                    if (!exist)
                    {
                        parameterisedInstances.Add(att);
                    }
                }
            }

            //Random r_ax = new Random(defaultSeed);

            //foreach (Attribute agentAtt in agents[0].actorType.attributes)//temp selected the first agent in the list
            //{
            //    if (agentAtt.domain.Count > 0)
            //    {
            //        agentAtt.activeDomain = agentAtt.domain[r_ax.Next(agentAtt.domain.Count)];

            //        bool exist = false;
            //        foreach (Object obj in parameterisedInstances)
            //        {
            //            Attribute attx = obj as Attribute;
            //            if (attx != null)
            //            {
            //                if (attx.name.Equals(agentAtt.name))
            //                {
            //                    exist = true;
            //                    break;
            //                }
            //            }
            //        }
            //        if (!exist)
            //        {
            //            parameterisedInstances.Add(agentAtt); 
            //        }

            //    }
            //}

            //this assignment ignores the current static assignment of agents to groups
            if (groupNames.Count>0 && groupFunctions.Count > 0)
            {
                foreach (Agent agent in agents)
                {
                    GroupFunction gf = new GroupFunction();

                    string groupFunction = groupFunctions[ra_gf.Next(groupFunctions.Count)];
                    string groupName = groupNames[ra_g.Next(groupNames.Count)];

                    gf.agentName = agent.agentName;
                    gf.functionName = groupFunction;
                    gf.groupName = groupName;

                    bool exist = false;
                    foreach (Object obj in parameterisedInstances)
                    {
                        GroupFunction gfx = obj as GroupFunction;
                        if (gfx != null)
                        {
                            if (gfx.functionName.Equals(gf.functionName)
                                && gfx.agentName.Equals(gf.agentName)
                                && gfx.groupName.Equals(gf.groupName))
                            {
                                exist = true;
                                break;
                            }
                        }
                    }
                    if (!exist)
                    {
                        parameterisedInstances.Add(gf);
                    }
                }
            }
            


            return parameterisedInstances;
        }

        //public List<string> randomAssignmentOfGroupFunctions()
        //{
        //    //this assignment ignores the current static assignment of agents to groups
        //    List<string> membershipFunctions = new List<string>();
        //    foreach (Agent agent in agents)
        //    {
        //        string groupFunction = groupFunctions[ra_gf.Next()];
        //        string groupName = groupNames[ra_g.Next()];

        //        string membershipFunction = groupFunction + " ( " + agent.agentName + " , " + groupName + " ) ";
        //        membershipFunctions.Add(membershipFunction);            
        //    }

        //    return membershipFunctions;
        //}

        public Dictionary<Dictionary<Agent, Agent>, ListDictionary> generateInteractionInstance(List<Object> parameterisedInstances, Agent sourceAgent)
        {
            //Dictionary<Dictionary<sendAgent,ReceiveAgent>>, Dictionary<subjectAgent, message>>
            Dictionary<Dictionary<Agent, Agent>, ListDictionary> transmission
                = new Dictionary<Dictionary<Agent, Agent>, ListDictionary>();
            
            Agent sendAgent = null;
            Agent receiveAgent = null;

            ListDictionary messageBody = null;
            Dictionary<Agent,Agent> messageHead = null;
            
            //set send agent properties
            int sendingAgentIndex = r_agent_vars.Next(agents.Count);
            sendAgent = agents[sendingAgentIndex];

            int seed = defaultSeed;
            //if (r_a == null)
            //{
            //    r_a = new Random(seed);
            //}

            foreach (Attribute agentAtt in sendAgent.actorType.attributes)
            {
                foreach (Object obj in parameterisedInstances)
                {
                    Attribute atte = obj as Attribute;
                    if (atte != null)
                    {
                        if (agentAtt.name.Equals(atte.name))
                        {
                            agentAtt.activeDomain = atte.activeDomain;
                        }
                    }
                } 
                //if (agentAtt.domain.Count > 0)
                //{
                    
                //}      
            }

            int seed_g = seed / 2;

            Random r_a_g = new Random(seed_g);
            sendAgent.activeGroupName = sendAgent.associatedGroups[r_a_g.Next(sendAgent.associatedGroups.Count)];

            //generate message
            if (seed % 2 == 0)//initiate a third-party transfer of message
            {
                //if (transmissionHistory.Count > 0)
                if (HistorySerialisationEngine.totalInteractions() > 0)
                {
                    messageBody = genSecondaryMessage(sendAgent, seed, seed, parameterisedInstances, sourceAgent);
                }
                else
                {
                    messageBody = genPrimaryMessage(sendAgent, seed, parameterisedInstances, sourceAgent);
                }
            }
            else
            {
                messageBody = genPrimaryMessage(sendAgent, seed, parameterisedInstances, sourceAgent);
            }
            
            //set receive agent properties
            int receivingAgentIndex = r_agent_vars.Next(agents.Count);
            receiveAgent = agents[receivingAgentIndex];
            while (receiveAgent.agentName.Equals(sendAgent.agentName))
            {
                receivingAgentIndex = r_agent_vars.Next(agents.Count);
                receiveAgent = agents[receivingAgentIndex];
            }

            //int seed1 = receivingAgentIndex / 2;
            //Random r_a1 = new Random(seed1);

            foreach (Attribute agentAtt in receiveAgent.actorType.attributes)
            {

                foreach (Object obj in parameterisedInstances)
                {
                    Attribute atte = obj as Attribute;
                    if (atte != null)
                    {
                        if (agentAtt.name.Equals(atte.name))
                        {
                            agentAtt.activeDomain = atte.activeDomain;
                            break;
                        }
                    }
                } 
                //if (agentAtt.domain.Count > 0)
                //{
                    
                //}
                    //agentAtt.activeDomain = agentAtt.domain[r_a1.Next(agentAtt.domain.Count)];
            }

            int seed_g1 = defaultSeed / 2;
            Random r_a_g1 = new Random(seed_g1);
            receiveAgent.activeGroupName = receiveAgent.associatedGroups[r_a_g1.Next(receiveAgent.associatedGroups.Count)];

            messageHead = new Dictionary<Agent,Agent>();
            messageHead.Add(sendAgent, receiveAgent);

            transmission.Add(messageHead, messageBody);

            return transmission;
        }

        private ListDictionary genSecondaryMessage(Agent sendAgent, int seed_x, int seed, List<Object> parameterisedInstances, Agent sourceAgent)
        {
            ListDictionary messageBody = null;
            List<Object> parameterisedInstances_h = null;
            List<Dictionary<Dictionary<Agent, Agent>, ListDictionary>> sendAgentIntHistory = new List<Dictionary<Dictionary<Agent, Agent>, ListDictionary>>();

            //Stack<string> stackedInteractionNames = HistorySerialisationEngine.getStackedInteractionList();
            Stack<int> interactionSeqNos = HistorySerialisationEngine.getStackedInteractionList();
            foreach (int interactionSeqNo in interactionSeqNos)
            {
                HistoryItem ih = HistorySerialisationEngine.deSerialiseInteractionHistory(interactionSeqNo, EmulationEnvironmentWindow.sourceAgentName);
                int counter = 0;
                while (ih == null && counter < 10)
                {
                    ih = HistorySerialisationEngine.deSerialiseInteractionHistory(interactionSeqNo, EmulationEnvironmentWindow.sourceAgentName);
                    counter++;
                }

                if (ih == null)
                    continue;
                ListDictionary historyItem = ih.interactionHistoryItem;
                foreach (DictionaryEntry th in historyItem)
                {
                    Dictionary<Dictionary<Agent, Agent>, ListDictionary> trans_h = (Dictionary<Dictionary<Agent, Agent>, ListDictionary>)th.Value;
                    parameterisedInstances_h = (List<Object>)th.Key;

                    foreach (KeyValuePair<Dictionary<Agent, Agent>, ListDictionary> pair in trans_h)
                    {
                        Dictionary<Agent, Agent> senderReceiver = pair.Key;
                        foreach (KeyValuePair<Agent, Agent> sr in senderReceiver)
                        {
                            Agent r_agent = sr.Value;
                            if (r_agent.agentName.Equals(sendAgent.agentName))
                            {
                                sendAgentIntHistory.Add(trans_h);
                            }
                        }
                    }
                }
            }

            if (sendAgentIntHistory.Count > 0)
            {
                int his_index = r_his_var.Next(sendAgentIntHistory.Count);

                Dictionary<Dictionary<Agent, Agent>, ListDictionary> thirdPTrans = sendAgentIntHistory[his_index];
                foreach (KeyValuePair<Dictionary<Agent, Agent>, ListDictionary> pair in thirdPTrans)
                {
                    messageBody = pair.Value;
                }

                //the new 3rd party sender is using same context as that of the subject
                //if (parameterisedInstances_h != null)
                //{
                //    foreach (DictionaryEntry de_h in messageBody)
                //    {
                //        Agent subjAgent = (Agent)de_h.Key;
                //        State activeState_h = de_h.Value as State;
                //        if (activeState_h != null)
                //        {
                //            Action activeAction_h = activeState_h.activeAction;
                //            if (activeAction_h.parameterisedOperationalAttributes.Count > 0)
                //            {
                //                foreach (Attribute att_h in activeAction_h.parameterisedOperationalAttributes)
                //                {
                //                    foreach (Object obj in parameterisedInstances)
                //                    {
                //                        Attribute atte = obj as Attribute;
                //                        if (atte != null)
                //                        {
                //                            if (att_h.name.Equals(atte.name))
                //                            {
                //                                atte.activeDomain = att_h.activeDomain;
                //                                parameterisedInstances.Remove(obj);
                //                                parameterisedInstances.Add(atte);
                //                                break;
                //                            }
                //                        }
                //                    }
                //                }
                //            }
                //        }
                //        else
                //        {
                //            Transition activeTransition_h = de_h.Value as Transition;
                //            if (activeTransition_h != null)
                //            {
                //                Event activeEvent_h = activeTransition_h.activeEvent;
                //                if (activeEvent_h.parameterisedOperationalAttributes.Count > 0)
                //                {
                //                    foreach (Attribute att_h in activeEvent_h.parameterisedOperationalAttributes)
                //                    {
                //                        foreach (Object obj in parameterisedInstances)
                //                        {
                //                            Attribute atte = obj as Attribute;
                //                            if (atte != null)
                //                            {
                //                                if (att_h.name.Equals(atte.name))
                //                                {
                //                                    atte.activeDomain = att_h.activeDomain;
                //                                    parameterisedInstances.Remove(obj);
                //                                    parameterisedInstances.Add(atte);
                //                                    break;
                //                                }
                //                            }
                //                        }
                //                    }
                //                }
                //            }
                //        }
                //    }
                //    //parameterisedInstances = parameterisedInstances_h;
                //}
            }
            else
            {
                messageBody = genPrimaryMessage(sendAgent, seed, parameterisedInstances, sourceAgent);
            }

            return messageBody;
        }

        private ListDictionary genPrimaryMessage(Agent sendAgent, int seed1, List<Object> parameterisedInstances, Agent sourceAgent)
        {
            Agent subjectAgent = null;
            State messageBody_s = null;
            Transition messageBody_t = null;
            ListDictionary message = null;

            if ((intCount % 3) == 0)
            {
                subjectAgent = sourceAgent;
            }
            else
            {
                subjectAgent = sendAgent;
            }
            intCount = intCount + 1;

            List<State> callableStates = new List<State>();
            foreach (State st in sendAgent.assignedMachine.states)
            {
                if (st.isCallable)
                {
                    callableStates.Add(st);
                }
            }

            List<Transition> callableTransitions = new List<Transition>();
            foreach (Transition trans in sendAgent.assignedMachine.transitions)
            {
                if (trans.isCallable)
                {
                    callableTransitions.Add(trans);
                }
            }

            
            if (callableTransitions.Count > 0)
            {
                int seed_val = r_sm_vars.Next(callableTransitions.Count);
                Transition activeTransition = callableTransitions[seed_val];
                parameterisedTransitionEventAtts(activeTransition, sendAgent.assignedMachine.machineName, parameterisedInstances);
                messageBody_t = activeTransition;
            }

            else if (callableStates.Count > 0)
            {
                if (seed1 % 2 == 0)
                {
                    int seed_val = -1;
                    seed_val = r_sm_vars.Next(callableStates.Count);

                    State activeState = callableStates[seed_val];
                    if (activeState.isCallable == true)
                    {
                        //sendAgent.assignedMachine.activeState = activeState;

                        if (activeState.actions.Count > 0)
                        {
                            string stateName = activeState.name;
                            List<Action> activableActions = new List<Action>();
                            foreach (Action act in activeState.actions)
                            {
                                string actionName = act.name;
                                PMisuseAn.behaviour.model.Action newAction = MisUseAnObjectsDB.loadStateActionDB(actionName, stateName, sendAgent.assignedMachine.machineName, MainWindow.clusterName);
                                if (newAction != null)
                                {
                                    if (newAction.operationalAttributes_.Count > 0)
                                    {
                                        parameteriseStateActionAtts(newAction, parameterisedInstances);
                                        activeState.activeAction = newAction;
                                        activableActions.Add(newAction);
                                    }
                                }
                            }
                            if (activableActions.Count > 0)
                            {
                                int seed_val_state_act = r_sm_vars.Next(activableActions.Count);
                                Action activeAction = activableActions[seed_val_state_act];
                                activeState.activeAction = activeAction;
                            }
                        }
                    }
                    messageBody_s = activeState;
                }
            }
            else
            {
                //no callable state or transition
            }

            if (messageBody_s != null)
            {
                message = new ListDictionary();
                message.Add(subjectAgent, messageBody_s);
            }
            else if (messageBody_t != null)
            {
                message = new ListDictionary();
                message.Add(subjectAgent, messageBody_t);
            }
            
            return message;
        }

        private void parameteriseStateActionAtts(Action action, List<Object> parameterisedInstances)
        {
            foreach (string attributeName in action.operationalAttributes_)
            {
                Attribute attribute = MisUseAnObjectsDB.loadAttributeFull(attributeName, MainWindow.clusterName);
                //int seed = environmentalAttributes.Count / 2;
                //Random r_a = new Random(seed);
                
                if (attribute == null)
                {
                    continue;
                }

                foreach (Object obj in parameterisedInstances)
                {
                    Attribute atte = obj as Attribute;
                    if (atte != null)
                    {
                        if (attribute.name.Equals(atte.name))
                        {
                            attribute.activeDomain = atte.activeDomain;
                            break;
                        }
                    }
                } 
                
                //if (attribute.domain.Count > 0)
                //{
                //    //attribute.activeDomain = attribute.domain[r_a.Next(attribute.domain.Count)];
                //}
                action.parameterisedOperationalAttributes.Add(attribute);
            }
        }

        private void parameterisedTransitionEventAtts(Transition transition, string machineName, List<Object> parameterisedInstances)
        {
            string associatedTransitionEventName = transition.associatedEevent;
            Event eve = MisUseAnObjectsDB.loadEventDB(associatedTransitionEventName,machineName, MainWindow.clusterName);
            foreach (string attributeName in eve.operationalAttributes_)
            {
                Attribute attribute = MisUseAnObjectsDB.loadAttributeFull(attributeName, MainWindow.clusterName);
                if (attribute == null)
                {
                    continue;
                }
                //int seed = environmentalAttributes.Count / 2;
                //Random r_a = new Random(seed);

                foreach (Object obj in parameterisedInstances)
                {
                    Attribute atte = obj as Attribute;
                    if (atte != null)
                    {
                        if (attribute.name.Equals(atte.name))
                        {
                            attribute.activeDomain = atte.activeDomain;
                            break;
                        }
                    }
                }

                //if (attribute.domain.Count > 0)
                //{
                //    attribute.activeDomain = attribute.domain[r_a.Next(attribute.domain.Count)];
                //}
                eve.parameterisedOperationalAttributes.Add(attribute);
            }
            transition.activeEvent = eve;        
        }

        public void setSensitivityObfuscationLevels(Attribute attribute)
        {
            int sens = r_agent_vars.Next(3);
            int bos = r_agent_vars.Next(4);
            if (attribute.sensitivityLevel == null)
            {
                if (sens == 1)
                {
                    attribute.sensitivityLevel = Attribute.Sensitivity.low;
                }
                else if (sens == 2)
                {
                    attribute.sensitivityLevel = Attribute.Sensitivity.medium;
                }
                else if (sens == 3)
                {
                    attribute.sensitivityLevel = Attribute.Sensitivity.high;
                }
            }

            if (attribute.obfuscationLevel == null)
            {
                if (bos == 1)
                {
                    attribute.obfuscationLevel = Attribute.Obfuscation.AccuratePrecise;
                }
                else if (bos == 2)
                {
                    attribute.obfuscationLevel = Attribute.Obfuscation.AccurateImprecise;
                }
                else if (bos == 3)
                {
                    attribute.obfuscationLevel = Attribute.Obfuscation.InaccuratePrecise;
                }
                else if (bos == 4)
                {
                    attribute.obfuscationLevel = Attribute.Obfuscation.InccurateImprecise;
                }
            }            
        }
    }
}
